home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / util / gnu / groff_src.lha / Groff-1.07 / tbl / table.cc < prev    next >
C/C++ Source or Header  |  1993-03-03  |  70KB  |  2,763 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include "table.h"
  22.  
  23. #define BAR_HEIGHT ".25m"
  24. #define DOUBLE_LINE_SEP "2p"
  25. #define HALF_DOUBLE_LINE_SEP "1p"
  26. #define LINE_SEP "2p"
  27. #define BODY_DEPTH ".25m"
  28.  
  29. const int DEFAULT_COLUMN_SEPARATION = 3;
  30.  
  31. #define DELIMITER_CHAR "\\[tbl]"
  32. #define PREFIX "3"
  33. #define SEPARATION_FACTOR_REG PREFIX "sep"
  34. #define BOTTOM_REG PREFIX "bot"
  35. #define RESET_MACRO_NAME PREFIX "init"
  36. #define LINESIZE_REG PREFIX "lps"
  37. #define TOP_REG PREFIX "top"
  38. #define CURRENT_ROW_REG PREFIX "crow"
  39. #define LAST_PASSED_ROW_REG PREFIX "passed"
  40. #define TRANSPARENT_STRING_NAME PREFIX "trans"
  41. #define QUOTE_STRING_NAME PREFIX "quote"
  42. #define SECTION_DIVERSION_NAME PREFIX "section"
  43. #define SECTION_DIVERSION_FLAG_REG PREFIX "sflag"
  44. #define SAVED_VERTICAL_POS_REG PREFIX "vert"
  45. #define NEED_BOTTOM_RULE_REG PREFIX "brule"
  46. #define KEEP_MACRO_NAME PREFIX "keep"
  47. #define RELEASE_MACRO_NAME PREFIX "release"
  48. #define SAVED_FONT_REG PREFIX "fnt"
  49. #define SAVED_SIZE_REG PREFIX "sz"
  50. #define SAVED_FILL_REG PREFIX "fll"
  51. #define SAVED_INDENT_REG PREFIX "ind"
  52. #define SAVED_CENTER_REG PREFIX "cent"
  53. #define TABLE_DIVERSION_NAME PREFIX "table"
  54. #define TABLE_DIVERSION_FLAG_REG PREFIX "tflag"
  55. #define TABLE_KEEP_MACRO_NAME PREFIX "tkeep"
  56. #define TABLE_RELEASE_MACRO_NAME PREFIX "trelease"
  57. #define NEEDED_REG PREFIX "needed"
  58. #define REPEATED_MARK_MACRO PREFIX "rmk"
  59. #define REPEATED_VPT_MACRO PREFIX "rvpt"
  60. #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
  61. #define SAVED_DN_REG PREFIX "dn"
  62.  
  63. // this must be one character
  64. #define COMPATIBLE_REG PREFIX "c"
  65.  
  66. #define BLOCK_WIDTH_PREFIX PREFIX "tbw"
  67. #define BLOCK_DIVERSION_PREFIX PREFIX "tbd"
  68. #define BLOCK_HEIGHT_PREFIX PREFIX "tbh"
  69. #define SPAN_WIDTH_PREFIX PREFIX "w"
  70. #define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw"
  71. #define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw"
  72. #define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw"
  73. #define COLUMN_SEPARATION_PREFIX PREFIX "cs"
  74. #define ROW_START_PREFIX PREFIX "rs"
  75. #define COLUMN_START_PREFIX PREFIX "cl"
  76. #define COLUMN_END_PREFIX PREFIX "ce"
  77. #define COLUMN_DIVIDE_PREFIX PREFIX "cd"
  78. #define ROW_TOP_PREFIX PREFIX "rt"
  79.  
  80. string block_width_reg(int r, int c);
  81. string block_diversion_name(int r, int c);
  82. string block_height_reg(int r, int c);
  83. string span_width_reg(int start_col, int end_col);
  84. string span_left_numeric_width_reg(int start_col, int end_col);
  85. string span_right_numeric_width_reg(int start_col, int end_col);
  86. string span_alphabetic_width_reg(int start_col, int end_col);
  87. string column_separation_reg(int col);
  88. string row_start_reg(int r);
  89. string column_start_reg(int c);
  90. string column_end_reg(int c);
  91. string column_divide_reg(int c);
  92. string row_top_reg(int r);
  93.  
  94. void set_inline_modifier(const entry_modifier *);
  95. void restore_inline_modifier(const entry_modifier *m);
  96. void set_modifier(const entry_modifier *);
  97. int find_dot(const char *s, const char *delim);
  98.  
  99. string an_empty_string;
  100. int location_force_filename = 0;
  101.  
  102. void printfs(const char *,
  103.          const string &arg1 = an_empty_string,
  104.          const string &arg2 = an_empty_string,
  105.          const string &arg3 = an_empty_string,
  106.          const string &arg4 = an_empty_string,
  107.          const string &arg5 = an_empty_string);
  108.  
  109. void prints(const string &);
  110.  
  111. inline void prints(char c)
  112. {
  113.   putchar(c);
  114. }
  115.  
  116. inline void prints(const char *s)
  117. {
  118.   fputs(s, stdout);
  119. }
  120.  
  121. void prints(const string &s)
  122. {
  123.   if (!s.empty())
  124.     fwrite(s.contents(), 1, s.length(), stdout);
  125. }
  126.  
  127. struct horizontal_span {
  128.   horizontal_span *next;
  129.   short start_col;
  130.   short end_col;
  131.   horizontal_span(int, int, horizontal_span *);
  132. };
  133.  
  134. struct single_line_entry;
  135. struct double_line_entry;
  136. struct simple_entry;
  137.  
  138. class table_entry {
  139. friend class table;
  140.   table_entry *next;
  141.   int input_lineno;
  142.   const char *input_filename;
  143. protected:
  144.   short start_row;
  145.   short start_col;
  146.   short end_row;
  147.   short end_col;
  148.   const entry_modifier *mod;
  149. public:
  150.   void set_location();
  151.   table_entry(const entry_modifier *);
  152.   virtual ~table_entry();
  153.   virtual int divert(int ncols, const string *mw, int *sep);
  154.   virtual void do_width();
  155.   virtual void do_depth();
  156.   virtual void print() = 0;
  157.   virtual void position_vertically() = 0;
  158.   virtual single_line_entry *to_single_line_entry();
  159.   virtual double_line_entry *to_double_line_entry();
  160.   virtual simple_entry *to_simple_entry();
  161.   virtual int line_type();
  162.   virtual void note_double_vrule_on_right(int);
  163.   virtual void note_double_vrule_on_left(int);
  164. };
  165.  
  166. class simple_entry : public table_entry {
  167. public:
  168.   simple_entry(const entry_modifier *);
  169.   void print();
  170.   void position_vertically();
  171.   simple_entry *to_simple_entry();
  172.   virtual void add_tab();
  173.   virtual void simple_print(int);
  174. };
  175.  
  176. class empty_entry : public simple_entry {
  177. public:
  178.   empty_entry(const entry_modifier *);
  179.   int line_type();
  180. };
  181.  
  182. class text_entry : public simple_entry {
  183. protected:
  184.   char *contents;
  185.   void print_contents();
  186. public:
  187.   text_entry(char *, const entry_modifier *);
  188.   ~text_entry();
  189. };
  190.  
  191. void text_entry::print_contents()
  192. {
  193.   set_inline_modifier(mod);
  194.   prints(contents);
  195.   restore_inline_modifier(mod);
  196. }
  197.  
  198. class repeated_char_entry : public text_entry {
  199. public:
  200.   repeated_char_entry(char *s, const entry_modifier *m);
  201.   void simple_print(int);
  202. };
  203.  
  204. class simple_text_entry : public text_entry {
  205. public:
  206.   simple_text_entry(char *s, const entry_modifier *m);
  207.   void do_width();
  208. };
  209.  
  210. class left_text_entry : public simple_text_entry {
  211. public:
  212.   left_text_entry(char *s, const entry_modifier *m);
  213.   void simple_print(int);
  214.   void add_tab();
  215. };
  216.  
  217. class right_text_entry : public simple_text_entry {
  218. public:
  219.   right_text_entry(char *s, const entry_modifier *m);
  220.   void simple_print(int);
  221.   void add_tab();
  222. };
  223.  
  224. class center_text_entry : public simple_text_entry {
  225. public:
  226.   center_text_entry(char *s, const entry_modifier *m);
  227.   void simple_print(int);
  228.   void add_tab();
  229. };
  230.  
  231. class numeric_text_entry : public text_entry {
  232.   int dot_pos;
  233. public:
  234.   numeric_text_entry(char *s, const entry_modifier *m, int pos);
  235.   void do_width();
  236.   void simple_print(int);
  237. };
  238.  
  239. class alphabetic_text_entry : public text_entry {
  240. public:
  241.   alphabetic_text_entry(char *s, const entry_modifier *m);
  242.   void do_width();
  243.   void simple_print(int);
  244.   void add_tab();
  245. };
  246.  
  247. class line_entry : public simple_entry {
  248. protected:
  249.   char double_vrule_on_right;
  250.   char double_vrule_on_left;
  251. public:
  252.   line_entry(const entry_modifier *);
  253.   void note_double_vrule_on_right(int);
  254.   void note_double_vrule_on_left(int);
  255.   void simple_print(int) = 0;
  256. };
  257.  
  258. class single_line_entry : public line_entry {
  259. public:
  260.   single_line_entry(const entry_modifier *m);
  261.   void simple_print(int);
  262.   single_line_entry *to_single_line_entry();
  263.   int line_type();
  264. };
  265.  
  266. class double_line_entry : public line_entry {
  267. public:
  268.   double_line_entry(const entry_modifier *m);
  269.   void simple_print(int);
  270.   double_line_entry *to_double_line_entry();
  271.   int line_type();
  272. };
  273.  
  274. class short_line_entry : public simple_entry {
  275. public:
  276.   short_line_entry(const entry_modifier *m);
  277.   void simple_print(int);
  278.   int line_type();
  279. };
  280.  
  281. class short_double_line_entry : public simple_entry {
  282. public:
  283.   short_double_line_entry(const entry_modifier *m);
  284.   void simple_print(int);
  285.   int line_type();
  286. };
  287.  
  288. class block_entry : public table_entry {
  289.   char *contents;
  290. protected:
  291.   void do_divert(int alphabetic, int ncols, const string *mw, int *sep);
  292. public:
  293.   block_entry(char *s, const entry_modifier *m);
  294.   ~block_entry();
  295.   int divert(int ncols, const string *mw, int *sep);
  296.   void do_width();
  297.   void do_depth();
  298.   void position_vertically();
  299.   void print() = 0;
  300. };
  301.  
  302. class left_block_entry : public block_entry {
  303. public:
  304.   left_block_entry(char *s, const entry_modifier *m);
  305.   void print();
  306. };
  307.  
  308. class right_block_entry : public block_entry {
  309. public:
  310.   right_block_entry(char *s, const entry_modifier *m);
  311.   void print();
  312. };
  313.  
  314. class center_block_entry : public block_entry {
  315. public:
  316.   center_block_entry(char *s, const entry_modifier *m);
  317.   void print();
  318. };
  319.  
  320. class alphabetic_block_entry : public block_entry {
  321. public:
  322.   alphabetic_block_entry(char *s, const entry_modifier *m);
  323.   void print();
  324.   int divert(int ncols, const string *mw, int *sep);
  325. };
  326.  
  327. table_entry::table_entry(const entry_modifier *m)
  328. : next(0), start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m),
  329.   input_lineno(-1), input_filename(0)
  330. {
  331. }
  332.  
  333. table_entry::~table_entry()
  334. {
  335. }
  336.  
  337. int table_entry::divert(int, const string *, int *)
  338. {
  339.   return 0;
  340. }
  341.  
  342. void table_entry::do_width()
  343. {
  344. }
  345.  
  346. single_line_entry *table_entry::to_single_line_entry()
  347. {
  348.   return 0;
  349. }
  350.  
  351. double_line_entry *table_entry::to_double_line_entry()
  352. {
  353.   return 0;
  354. }
  355.  
  356. simple_entry *table_entry::to_simple_entry()
  357. {
  358.   return 0;
  359. }
  360.  
  361. void table_entry::do_depth()
  362. {
  363. }
  364.  
  365. void table_entry::set_location()
  366. {
  367.   set_troff_location(input_filename, input_lineno);
  368. }
  369.  
  370. int table_entry::line_type()
  371. {
  372.   return -1;
  373. }
  374.  
  375. void table_entry::note_double_vrule_on_right(int)
  376. {
  377. }
  378.  
  379. void table_entry::note_double_vrule_on_left(int)
  380. {
  381. }
  382.  
  383. simple_entry::simple_entry(const entry_modifier *m) : table_entry(m)
  384. {
  385. }
  386.  
  387. void simple_entry::add_tab()
  388. {
  389.   // do nothing
  390. }
  391.  
  392. void simple_entry::simple_print(int)
  393. {
  394.   // do nothing
  395. }
  396.  
  397. void simple_entry::position_vertically()
  398. {
  399.   if (start_row != end_row)
  400.     switch (mod->vertical_alignment) {
  401.     case entry_modifier::TOP:
  402.       printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
  403.       break;
  404.     case entry_modifier::CENTER:
  405.       // Peform the motion in two stages so that the center is rounded
  406.       // vertically upwards even if net vertical motion is upwards.
  407.       printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
  408.       printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n", 
  409.           row_start_reg(start_row));
  410.       break;
  411.     case entry_modifier::BOTTOM:
  412.       printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n", 
  413.           row_start_reg(start_row));
  414.       break;
  415.     default:
  416.       assert(0);
  417.     }
  418. }
  419.  
  420. void simple_entry::print()
  421. {
  422.   prints(".ta");
  423.   add_tab();
  424.   prints('\n');
  425.   set_location();
  426.   prints("\\&");
  427.   simple_print(0);
  428.   prints('\n');
  429. }
  430.  
  431. simple_entry *simple_entry::to_simple_entry()
  432. {
  433.   return this;
  434. }
  435.  
  436. empty_entry::empty_entry(const entry_modifier *m)
  437. : simple_entry(m)
  438. {
  439. }
  440.  
  441. int empty_entry::line_type()
  442. {
  443.   return 0;
  444. }
  445.  
  446. text_entry::text_entry(char *s, const entry_modifier *m)
  447. : contents(s), simple_entry(m)
  448. {
  449. }
  450.  
  451. text_entry::~text_entry()
  452. {
  453.   a_delete contents;
  454. }
  455.  
  456.  
  457. repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m)
  458. : text_entry(s, m)
  459. {
  460. }
  461.  
  462. void repeated_char_entry::simple_print(int)
  463. {
  464.   printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
  465.   set_inline_modifier(mod);
  466.   printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&",
  467.       span_width_reg(start_col, end_col));
  468.   prints(contents);
  469.   prints(DELIMITER_CHAR);
  470.   restore_inline_modifier(mod);
  471. }
  472.  
  473. simple_text_entry::simple_text_entry(char *s, const entry_modifier *m)
  474. : text_entry(s, m)
  475. {
  476. }
  477.  
  478. void simple_text_entry::do_width()
  479. {
  480.   set_location();
  481.   printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
  482.       span_width_reg(start_col, end_col));
  483.   print_contents();
  484.   prints(DELIMITER_CHAR "\n");
  485. }
  486.  
  487. left_text_entry::left_text_entry(char *s, const entry_modifier *m)
  488. : simple_text_entry(s, m)
  489. {
  490. }
  491.  
  492. void left_text_entry::simple_print(int)
  493. {
  494.   printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
  495.   print_contents();
  496. }
  497.  
  498. // The only point of this is to make `\a' ``work'' as in Unix tbl.  Grrr.
  499.  
  500. void left_text_entry::add_tab()
  501. {
  502.   printfs(" \\n[%1]u", column_end_reg(end_col));
  503. }
  504.  
  505. right_text_entry::right_text_entry(char *s, const entry_modifier *m)
  506. : simple_text_entry(s, m)
  507. {
  508. }
  509.  
  510. void right_text_entry::simple_print(int)
  511. {
  512.   printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
  513.   prints("\002\003");
  514.   print_contents();
  515.   prints("\002");
  516. }
  517.  
  518. void right_text_entry::add_tab()
  519. {
  520.   printfs(" \\n[%1]u", column_end_reg(end_col));
  521. }
  522.  
  523. center_text_entry::center_text_entry(char *s, const entry_modifier *m)
  524. : simple_text_entry(s, m)
  525. {
  526. }
  527.  
  528. void center_text_entry::simple_print(int)
  529. {
  530.   printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
  531.   prints("\002\003");
  532.   print_contents();
  533.   prints("\003\002");
  534. }
  535.  
  536. void center_text_entry::add_tab()
  537. {
  538.   printfs(" \\n[%1]u", column_end_reg(end_col));
  539. }
  540.  
  541. numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos)
  542. : text_entry(s, m), dot_pos(pos)
  543. {
  544. }
  545.  
  546. void numeric_text_entry::do_width()
  547. {
  548.   if (dot_pos != 0) {
  549.     set_location();
  550.     printfs(".nr %1 0\\w" DELIMITER_CHAR,
  551.         block_width_reg(start_row, start_col));
  552.     set_inline_modifier(mod);
  553.     for (int i = 0; i < dot_pos; i++)
  554.       prints(contents[i]);
  555.     restore_inline_modifier(mod);
  556.     prints(DELIMITER_CHAR "\n");
  557.     printfs(".nr %1 \\n[%1]>?\\n[%2]\n",
  558.         span_left_numeric_width_reg(start_col, end_col),
  559.         block_width_reg(start_row, start_col));
  560.   }
  561.   else
  562.     printfs(".nr %1 0\n", block_width_reg(start_row, start_col));
  563.   if (contents[dot_pos] != '\0') {
  564.     set_location();
  565.     printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
  566.         span_right_numeric_width_reg(start_col, end_col));
  567.     set_inline_modifier(mod);
  568.     prints(contents + dot_pos);
  569.     restore_inline_modifier(mod);
  570.     prints(DELIMITER_CHAR "\n");
  571.   }
  572. }
  573.  
  574. void numeric_text_entry::simple_print(int)
  575. {
  576.   printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'",
  577.       span_width_reg(start_col, end_col),
  578.       span_left_numeric_width_reg(start_col, end_col),
  579.       span_right_numeric_width_reg(start_col, end_col),
  580.       column_start_reg(start_col),
  581.       block_width_reg(start_row, start_col));
  582.   print_contents();
  583. }
  584.  
  585. alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m)
  586. : text_entry(s, m)
  587. {
  588. }
  589.  
  590. void alphabetic_text_entry::do_width()
  591. {
  592.   set_location();
  593.   printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR,
  594.       span_alphabetic_width_reg(start_col, end_col));
  595.   print_contents();
  596.   prints(DELIMITER_CHAR "\n");
  597. }
  598.  
  599. void alphabetic_text_entry::simple_print(int)
  600. {
  601.   printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
  602.   printfs("\\h'\\n[%1]u-\\n[%2]u/2u'",
  603.       span_width_reg(start_col, end_col),
  604.       span_alphabetic_width_reg(start_col, end_col));
  605.   print_contents();
  606. }
  607.  
  608. // The only point of this is to make `\a' ``work'' as in Unix tbl.  Grrr.
  609.  
  610. void alphabetic_text_entry::add_tab()
  611. {
  612.   printfs(" \\n[%1]u", column_end_reg(end_col));
  613. }
  614.  
  615. block_entry::block_entry(char *s, const entry_modifier *m)
  616. : table_entry(m), contents(s)
  617. {
  618. }
  619.  
  620. block_entry::~block_entry()
  621. {
  622.   a_delete contents;
  623. }
  624.  
  625. void block_entry::position_vertically()
  626. {
  627.   if (start_row != end_row)
  628.     switch(mod->vertical_alignment) {
  629.     case entry_modifier::TOP:
  630.       printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
  631.       break;
  632.     case entry_modifier::CENTER:
  633.       // Peform the motion in two stages so that the center is rounded
  634.       // vertically upwards even if net vertical motion is upwards.
  635.       printfs(".sp |\\n[%1]u\n", row_start_reg(start_row));
  636.       printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n", 
  637.           row_start_reg(start_row),
  638.           block_height_reg(start_row, start_col));
  639.       break;
  640.     case entry_modifier::BOTTOM:
  641.       printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n", 
  642.           row_start_reg(start_row),
  643.           block_height_reg(start_row, start_col));
  644.       break;
  645.     default:
  646.       assert(0);
  647.     }
  648.   if (mod->stagger)
  649.     prints(".sp -.5v\n");
  650. }
  651.  
  652. int block_entry::divert(int ncols, const string *mw, int *sep)
  653. {
  654.   do_divert(0, ncols, mw, sep);
  655.   return 1;
  656. }
  657.  
  658. void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
  659.                 int *sep)
  660. {
  661.   printfs(".di %1\n", block_diversion_name(start_row, start_col));
  662.   prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
  663.      ".in 0\n");
  664.   prints(".ll ");
  665.   for (int i = start_col; i <= end_col; i++)
  666.     if (mw[i].empty())
  667.       break;
  668.   if (i > end_col) {
  669.     // Every column spanned by this entry has a minimum width.
  670.     for (int i = start_col; i <= end_col; i++) {
  671.       if (i > start_col) {
  672.     if (sep)
  673.       printfs("+%1n", as_string(sep[i - 1]));
  674.     prints('+');
  675.       }
  676.       printfs("(n;%1)", mw[i]);
  677.     }
  678.     printfs(">?\\n[%1]u", span_width_reg(start_col, end_col));
  679.   }
  680.   else
  681.     printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))", 
  682.         span_width_reg(start_col, end_col), 
  683.         as_string(end_col - start_col + 1),
  684.         as_string(ncols + 1));
  685.   if (alphabetic)
  686.     prints("-2n");
  687.   prints("\n");
  688.   set_modifier(mod);
  689.   prints(".cp \\n(" COMPATIBLE_REG "\n");
  690.   set_location();
  691.   prints(contents);
  692.   prints(".br\n.di\n.cp 0\n");
  693.   if (!mod->zero_width) {
  694.     if (alphabetic) {
  695.       printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n",
  696.           span_width_reg(start_col, end_col));
  697.       printfs(".nr %1 \\n[%1]>?\\n[dl]\n",
  698.           span_alphabetic_width_reg(start_col, end_col));
  699.     }
  700.     else
  701.       printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col));
  702.   }
  703.   printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col));
  704.   printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col));
  705.   prints("." RESET_MACRO_NAME "\n"
  706.      ".in \\n[" SAVED_INDENT_REG "]u\n"
  707.      ".nf\n");
  708.   // the block might have contained .lf commands
  709.   location_force_filename = 1;
  710. }
  711.  
  712. void block_entry::do_width()
  713. {
  714.   // do nothing; the action happens in divert
  715. }
  716.  
  717. void block_entry::do_depth()
  718. {
  719.   printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n",
  720.       row_start_reg(start_row),
  721.       block_height_reg(start_row, start_col));
  722. }
  723.  
  724. left_block_entry::left_block_entry(char *s, const entry_modifier *m)
  725. : block_entry(s, m)
  726. {
  727. }
  728.  
  729. void left_block_entry::print()
  730. {
  731.   printfs(".in +\\n[%1]u\n", column_start_reg(start_col));
  732.   printfs(".%1\n", block_diversion_name(start_row, start_col));
  733.   prints(".in\n");
  734. }
  735.  
  736.  
  737.  
  738. right_block_entry::right_block_entry(char *s, const entry_modifier *m)
  739. : block_entry(s, m)
  740. {
  741. }
  742.  
  743. void right_block_entry::print()
  744. {
  745.   printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n",
  746.       column_start_reg(start_col),
  747.       span_width_reg(start_col, end_col),
  748.       block_width_reg(start_row, start_col));
  749.   printfs(".%1\n", block_diversion_name(start_row, start_col));
  750.   prints(".in\n");
  751. }
  752.  
  753. center_block_entry::center_block_entry(char *s, const entry_modifier *m)
  754. : block_entry(s, m)
  755. {
  756. }
  757.  
  758. void center_block_entry::print()
  759. {
  760.   printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
  761.       column_start_reg(start_col),
  762.       span_width_reg(start_col, end_col),
  763.       block_width_reg(start_row, start_col));
  764.   printfs(".%1\n", block_diversion_name(start_row, start_col));
  765.   prints(".in\n");
  766. }
  767.  
  768. alphabetic_block_entry::alphabetic_block_entry(char *s,
  769.                            const entry_modifier *m)
  770. : block_entry(s, m)
  771. {
  772. }
  773.  
  774. int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep)
  775. {
  776.   do_divert(1, ncols, mw, sep);
  777.   return 1;
  778. }
  779.  
  780. void alphabetic_block_entry::print()
  781. {
  782.   printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n",
  783.       column_start_reg(start_col),
  784.       span_width_reg(start_col, end_col),
  785.       span_alphabetic_width_reg(start_col, end_col));
  786.   printfs(".%1\n", block_diversion_name(start_row, start_col));
  787.   prints(".in\n");
  788. }
  789.  
  790. line_entry::line_entry(const entry_modifier *m)
  791. : simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0)
  792. {
  793. }
  794.  
  795. void line_entry::note_double_vrule_on_right(int is_corner)
  796. {
  797.   double_vrule_on_right = is_corner ? 1 : 2;
  798. }
  799.  
  800. void line_entry::note_double_vrule_on_left(int is_corner)
  801. {
  802.   double_vrule_on_left = is_corner ? 1 : 2;
  803. }
  804.  
  805.  
  806. single_line_entry::single_line_entry(const entry_modifier *m)
  807. : line_entry(m)
  808. {
  809. }
  810.  
  811. int single_line_entry::line_type()
  812. {
  813.   return 1;
  814. }
  815.  
  816. void single_line_entry::simple_print(int dont_move)
  817. {
  818.   printfs("\\h'|\\n[%1]u",
  819.       column_divide_reg(start_col));
  820.   if (double_vrule_on_left) {
  821.     prints(double_vrule_on_left == 1 ? "-" : "+");
  822.     prints(HALF_DOUBLE_LINE_SEP);
  823.   }
  824.   prints("'");
  825.   if (!dont_move)
  826.     prints("\\v'-" BAR_HEIGHT "'");
  827.   printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u",
  828.       column_divide_reg(end_col+1));
  829.   if (double_vrule_on_right) {
  830.     prints(double_vrule_on_left == 1 ? "+" : "-");
  831.     prints(HALF_DOUBLE_LINE_SEP);
  832.   }
  833.   prints("0'\\s0");
  834.   if (!dont_move)
  835.     prints("\\v'" BAR_HEIGHT "'");
  836. }
  837.   
  838. single_line_entry *single_line_entry::to_single_line_entry()
  839. {
  840.   return this;
  841. }
  842.  
  843. double_line_entry::double_line_entry(const entry_modifier *m)
  844. : line_entry(m)
  845. {
  846. }
  847.  
  848. int double_line_entry::line_type()
  849. {
  850.   return 2;
  851. }
  852.  
  853. void double_line_entry::simple_print(int dont_move)
  854. {
  855.   if (!dont_move)
  856.     prints("\\v'-" BAR_HEIGHT "'");
  857.   printfs("\\h'|\\n[%1]u",
  858.       column_divide_reg(start_col));
  859.   if (double_vrule_on_left) {
  860.     prints(double_vrule_on_left == 1 ? "-" : "+");
  861.     prints(HALF_DOUBLE_LINE_SEP);
  862.   }
  863.   prints("'");
  864.   printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'"
  865.       "\\s[\\n[" LINESIZE_REG "]]"
  866.       "\\D'l |\\n[%1]u",
  867.       column_divide_reg(end_col+1));
  868.   if (double_vrule_on_right)
  869.     prints("-" HALF_DOUBLE_LINE_SEP);
  870.   prints(" 0'");
  871.   printfs("\\v'" DOUBLE_LINE_SEP "'"
  872.       "\\D'l |\\n[%1]u",
  873.       column_divide_reg(start_col));
  874.   if (double_vrule_on_right) {
  875.     prints(double_vrule_on_left == 1 ? "+" : "-");
  876.     prints(HALF_DOUBLE_LINE_SEP);
  877.   }
  878.   prints(" 0'");
  879.   prints("\\s0"
  880.      "\\v'-" HALF_DOUBLE_LINE_SEP "'");
  881.   if (!dont_move)
  882.     prints("\\v'" BAR_HEIGHT "'");
  883. }
  884.  
  885. double_line_entry *double_line_entry::to_double_line_entry()
  886. {
  887.   return this;
  888. }
  889.  
  890. short_line_entry::short_line_entry(const entry_modifier *m)
  891. : simple_entry(m)
  892. {
  893. }
  894.  
  895. int short_line_entry::line_type()
  896. {
  897.   return 1;
  898. }
  899.  
  900. void short_line_entry::simple_print(int dont_move)
  901. {
  902.   if (mod->stagger)
  903.     prints("\\v'-.5v'");
  904.   if (!dont_move)
  905.     prints("\\v'-" BAR_HEIGHT "'");
  906.   printfs("\\h'|\\n[%1]u'", column_start_reg(start_col));
  907.   printfs("\\s[\\n[" LINESIZE_REG "]]"
  908.       "\\D'l \\n[%1]u 0'"
  909.       "\\s0",
  910.       span_width_reg(start_col, end_col));
  911.   if (!dont_move)
  912.     prints("\\v'" BAR_HEIGHT "'");
  913.   if (mod->stagger)
  914.     prints("\\v'.5v'");
  915. }
  916.  
  917. short_double_line_entry::short_double_line_entry(const entry_modifier *m)
  918. : simple_entry(m)
  919. {
  920. }
  921.  
  922. int short_double_line_entry::line_type()
  923. {
  924.   return 2;
  925. }
  926.  
  927. void short_double_line_entry::simple_print(int dont_move)
  928. {
  929.   if (mod->stagger)
  930.     prints("\\v'-.5v'");
  931.   if (!dont_move)
  932.     prints("\\v'-" BAR_HEIGHT "'");
  933.   printfs("\\h'|\\n[%2]u'"
  934.       "\\v'-" HALF_DOUBLE_LINE_SEP "'"
  935.       "\\s[\\n[" LINESIZE_REG "]]"
  936.       "\\D'l \\n[%1]u 0'"
  937.       "\\v'" DOUBLE_LINE_SEP "'"
  938.       "\\D'l |\\n[%2]u 0'"
  939.       "\\s0"
  940.       "\\v'-" HALF_DOUBLE_LINE_SEP "'",
  941.       span_width_reg(start_col, end_col),
  942.       column_start_reg(start_col));
  943.   if (!dont_move)
  944.     prints("\\v'" BAR_HEIGHT "'");
  945.   if (mod->stagger)
  946.     prints("\\v'.5v'");
  947. }
  948.  
  949. void set_modifier(const entry_modifier *m)
  950. {
  951.   if (!m->font.empty())
  952.     printfs(".ft %1\n", m->font);
  953.   if (m->point_size.val != 0) {
  954.     prints(".ps ");
  955.     if (m->point_size.inc > 0)
  956.       prints('+');
  957.     else if (m->point_size.inc < 0)
  958.       prints('-');
  959.     printfs("%1\n", as_string(m->point_size.val));
  960.   }
  961.   if (m->vertical_spacing.val != 0) {
  962.     prints(".vs ");
  963.     if (m->vertical_spacing.inc > 0)
  964.       prints('+');
  965.     else if (m->vertical_spacing.inc < 0)
  966.       prints('-');
  967.     printfs("%1\n", as_string(m->vertical_spacing.val));
  968.   }
  969. }
  970.  
  971. void set_inline_modifier(const entry_modifier *m)
  972. {
  973.   if (!m->font.empty())
  974.     printfs("\\f[%1]", m->font);
  975.   if (m->point_size.val != 0) {
  976.     prints("\\s[");
  977.     if (m->point_size.inc > 0)
  978.       prints('+');
  979.     else if (m->point_size.inc < 0)
  980.       prints('-');
  981.     printfs("%1]", as_string(m->point_size.val));
  982.   }
  983.   if (m->stagger)
  984.     prints("\\v'-.5v'");
  985. }
  986.  
  987. void restore_inline_modifier(const entry_modifier *m)
  988. {
  989.   if (!m->font.empty())
  990.     prints("\\f[\\n[" SAVED_FONT_REG "]]");
  991.   if (m->point_size.val != 0)
  992.     prints("\\s[\\n[" SAVED_SIZE_REG "]]");
  993.   if (m->stagger)
  994.     prints("\\v'.5v'");
  995. }
  996.  
  997.  
  998. struct stuff {
  999.   stuff *next;
  1000.   int row;            // occurs before row `row'
  1001.   char printed;            // has it been printed?
  1002.  
  1003.   stuff(int);
  1004.   virtual void print(table *) = 0;
  1005.   virtual ~stuff();
  1006.   virtual int is_single_line() { return 0; };
  1007.   virtual int is_double_line() { return 0; };
  1008. };
  1009.  
  1010. stuff::stuff(int r) : row(r), next(0), printed(0)
  1011. {
  1012. }
  1013.  
  1014. stuff::~stuff()
  1015. {
  1016. }
  1017.  
  1018. struct text_stuff : stuff {
  1019.   string contents;
  1020.   const char *filename;
  1021.   int lineno;
  1022.  
  1023.   text_stuff(const string &, int r, const char *fn, int ln);
  1024.   ~text_stuff();
  1025.   void print(table *);
  1026. };
  1027.  
  1028.  
  1029. text_stuff::text_stuff(const string &s, int r, const char *fn, int ln)
  1030. : contents(s), stuff(r), filename(fn), lineno(ln)
  1031. {
  1032. }
  1033.  
  1034. text_stuff::~text_stuff()
  1035. {
  1036. }
  1037.  
  1038. void text_stuff::print(table *)
  1039. {
  1040.   printed = 1;
  1041.   prints(".cp \\n(" COMPATIBLE_REG "\n");
  1042.   set_troff_location(filename, lineno);
  1043.   prints(contents);
  1044.   prints(".cp 0\n");
  1045.   location_force_filename = 1;    // it might have been a .lf command
  1046. }
  1047.  
  1048. struct single_hline_stuff : stuff {
  1049.   single_hline_stuff(int r);
  1050.   void print(table *);
  1051.   int is_single_line();
  1052. };
  1053.  
  1054. single_hline_stuff::single_hline_stuff(int r) : stuff(r)
  1055. {
  1056. }
  1057.  
  1058. void single_hline_stuff::print(table *tbl)
  1059. {
  1060.   printed = 1;
  1061.   tbl->print_single_hline(row);
  1062. }
  1063.  
  1064. int single_hline_stuff::is_single_line()
  1065. {
  1066.   return 1;
  1067. }
  1068.  
  1069. struct double_hline_stuff : stuff {
  1070.   double_hline_stuff(int r);
  1071.   void print(table *);
  1072.   int is_double_line();
  1073. };
  1074.  
  1075. double_hline_stuff::double_hline_stuff(int r) : stuff(r)
  1076. {
  1077. }
  1078.  
  1079. void double_hline_stuff::print(table *tbl)
  1080. {
  1081.   printed = 1;
  1082.   tbl->print_double_hline(row);
  1083. }
  1084.  
  1085. int double_hline_stuff::is_double_line()
  1086. {
  1087.   return 1;
  1088. }
  1089.  
  1090. struct vertical_rule {
  1091.   vertical_rule *next;
  1092.   short start_row;
  1093.   short end_row;
  1094.   short col;
  1095.   char is_double;
  1096.   string top_adjust;
  1097.   string bot_adjust;
  1098.  
  1099.   vertical_rule(int sr, int er, int c, int dbl, vertical_rule *);
  1100.   ~vertical_rule();
  1101.   void contribute_to_bottom_macro(table *);
  1102.   void print();
  1103. };
  1104.  
  1105. vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p)
  1106. : start_row(sr), end_row(er), col(c), is_double(dbl), next(p)
  1107. {
  1108. }
  1109.  
  1110. vertical_rule::~vertical_rule()
  1111. {
  1112. }
  1113.  
  1114. void vertical_rule::contribute_to_bottom_macro(table *tbl)
  1115. {
  1116.   printfs(".if \\n[" CURRENT_ROW_REG "]>=%1",
  1117.       as_string(start_row));
  1118.   if (end_row != tbl->get_nrows() - 1)
  1119.     printfs("&(\\n[" CURRENT_ROW_REG "]<%1)",
  1120.         as_string(end_row));
  1121.   prints(" \\{");
  1122.   printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n",
  1123.       as_string(start_row),
  1124.       row_top_reg(start_row));
  1125.   const char *offset_table[3];
  1126.   if (is_double) {
  1127.     offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
  1128.     offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
  1129.     offset_table[2] = 0;
  1130.   }
  1131.   else {
  1132.     offset_table[0] = "";
  1133.     offset_table[1] = 0;
  1134.   }
  1135.   for (const char **offsetp = offset_table; *offsetp; offsetp++) {
  1136.     prints(".sp -1\n"
  1137.        "\\v'" BODY_DEPTH);
  1138.     if (!bot_adjust.empty())
  1139.       printfs("+%1", bot_adjust);
  1140.     prints("'");
  1141.     printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v",
  1142.         column_divide_reg(col),
  1143.         row_top_reg(start_row),
  1144.         *offsetp);
  1145.     if (!bot_adjust.empty())
  1146.       printfs("-(%1)", bot_adjust);
  1147.     // don't perform the top adjustment if the top is actually #T
  1148.     if (!top_adjust.empty())
  1149.       printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))",
  1150.           top_adjust,
  1151.           as_string(start_row));
  1152.     prints("'\\s0\n");
  1153.   }
  1154.   prints(".\\}\n");
  1155. }
  1156.  
  1157. void vertical_rule::print()
  1158. {
  1159.   printfs("\\*[" TRANSPARENT_STRING_NAME "]"
  1160.       ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] "
  1161.       ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n",
  1162.       as_string(start_row),
  1163.       row_top_reg(start_row));
  1164.   const char *offset_table[3];
  1165.   if (is_double) {
  1166.     offset_table[0] = "-" HALF_DOUBLE_LINE_SEP;
  1167.     offset_table[1] = "+" HALF_DOUBLE_LINE_SEP;
  1168.     offset_table[2] = 0;
  1169.   }
  1170.   else {
  1171.     offset_table[0] = "";
  1172.     offset_table[1] = 0;
  1173.   }
  1174.   for (const char **offsetp = offset_table; *offsetp; offsetp++) {
  1175.     prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n"
  1176.        "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH);
  1177.     if (!bot_adjust.empty())
  1178.       printfs("+%1", bot_adjust);
  1179.     prints("'");
  1180.     printfs("\\h'\\n[%1]u%3'"
  1181.         "\\s[\\n[" LINESIZE_REG "]]"
  1182.         "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v",
  1183.         column_divide_reg(col),
  1184.         row_top_reg(start_row),
  1185.         *offsetp);
  1186.     if (!bot_adjust.empty())
  1187.       printfs("-(%1)", bot_adjust);
  1188.     // don't perform the top adjustment if the top is actually #T
  1189.     if (!top_adjust.empty())
  1190.       printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n["
  1191.           LAST_PASSED_ROW_REG "]))",
  1192.           top_adjust,
  1193.           as_string(start_row));
  1194.     prints("'"
  1195.        "\\s0\n");
  1196.   }
  1197. }
  1198.  
  1199. table::table(int nc, unsigned f, int ls)
  1200. : ncolumns(nc), flags(f), linesize(ls),
  1201.   nrows(0), allocated_rows(0), entry(0), entry_list(0),
  1202.   left_separation(0), right_separation(0), stuff_list(0), vline(0),
  1203.   vrule_list(0), row_is_all_lines(0), span_list(0)
  1204. {
  1205.   minimum_width = new string[ncolumns];
  1206.   column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
  1207.   equal = new char[ncolumns];
  1208.   int i;
  1209.   for (i = 0; i < ncolumns; i++)
  1210.     equal[i] = 0;
  1211.   for (i = 0; i < ncolumns-1; i++)
  1212.     column_separation[i] = DEFAULT_COLUMN_SEPARATION;
  1213.   delim[0] = delim[1] = '\0';
  1214. }
  1215.  
  1216. table::~table()
  1217. {
  1218.   for (int i = 0; i < nrows; i++) {
  1219.     a_delete entry[i];
  1220.     a_delete vline[i];
  1221.   }
  1222.   a_delete entry;
  1223.   a_delete vline;
  1224.   while (entry_list) {
  1225.     table_entry *tem = entry_list;
  1226.     entry_list = entry_list->next;
  1227.     delete tem;
  1228.   }
  1229.   ad_delete(ncolumns) minimum_width;
  1230.   a_delete column_separation;
  1231.   a_delete equal;
  1232.   while (stuff_list) {
  1233.     stuff *tem = stuff_list;
  1234.     stuff_list = stuff_list->next;
  1235.     delete tem;
  1236.   }
  1237.   while (vrule_list) {
  1238.     vertical_rule *tem = vrule_list;
  1239.     vrule_list = vrule_list->next;
  1240.     delete tem;
  1241.   }
  1242.   a_delete row_is_all_lines;
  1243.   while (span_list) {
  1244.     horizontal_span *tem = span_list;
  1245.     span_list = span_list->next;
  1246.     delete tem;
  1247.   }
  1248. }
  1249.  
  1250. void table::set_delim(char c1, char c2)
  1251. {
  1252.   delim[0] = c1;
  1253.   delim[1] = c2;
  1254. }
  1255.  
  1256. void table::set_minimum_width(int c, const string &w)
  1257. {
  1258.   assert(c >= 0 && c < ncolumns);
  1259.   minimum_width[c] = w;
  1260. }
  1261.  
  1262. void table::set_column_separation(int c, int n)
  1263. {
  1264.   assert(c >= 0 && c < ncolumns - 1);
  1265.   column_separation[c] = n;
  1266. }
  1267.  
  1268. void table::set_equal_column(int c)
  1269. {
  1270.   assert(c >= 0 && c < ncolumns);
  1271.   equal[c] = 1;
  1272. }
  1273.  
  1274. void table::add_stuff(stuff *p)
  1275. {
  1276.   for (stuff **pp = &stuff_list; *pp; pp = &(*pp)->next)
  1277.     ;
  1278.   *pp = p;
  1279. }
  1280.  
  1281. void table::add_text_line(int r, const string &s, const char *filename, int lineno)
  1282. {
  1283.   add_stuff(new text_stuff(s, r, filename, lineno));
  1284. }
  1285.  
  1286. void table::add_single_hline(int r)
  1287. {
  1288.   add_stuff(new single_hline_stuff(r));
  1289. }
  1290.  
  1291. void table::add_double_hline(int r)
  1292. {
  1293.   add_stuff(new double_hline_stuff(r));
  1294. }
  1295.  
  1296. void table::allocate(int r)
  1297. {
  1298.   if (r >= nrows) {
  1299.     typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug
  1300.     if (r >= allocated_rows) {
  1301.       if (allocated_rows == 0) {
  1302.     allocated_rows = 16;
  1303.     if (allocated_rows <= r)
  1304.       allocated_rows = r + 1;
  1305.     entry = new PPtable_entry[allocated_rows];
  1306.     vline = new char*[allocated_rows];
  1307.       }
  1308.       else {
  1309.     table_entry ***old_entry = entry;
  1310.     int old_allocated_rows = allocated_rows;
  1311.     allocated_rows *= 2;
  1312.     if (allocated_rows <= r)
  1313.       allocated_rows = r + 1;
  1314.     entry = new PPtable_entry[allocated_rows];
  1315.     memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows);
  1316.     a_delete old_entry;
  1317.     char **old_vline = vline;
  1318.     vline = new char*[allocated_rows];
  1319.     memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows);
  1320.     a_delete old_vline;
  1321.       }
  1322.     }
  1323.     assert(allocated_rows > r);
  1324.     while (nrows <= r) {
  1325.       entry[nrows] = new table_entry*[ncolumns];
  1326.       for (int i = 0; i < ncolumns; i++)
  1327.     entry[nrows][i] = 0;
  1328.       vline[nrows] = new char[ncolumns+1];
  1329.       for (i = 0; i < ncolumns+1; i++)
  1330.     vline[nrows][i] = 0;
  1331.       nrows++;
  1332.     }
  1333.   }
  1334. }
  1335.  
  1336. void table::do_hspan(int r, int c)
  1337. {
  1338.   assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
  1339.   if (c == 0) {
  1340.     error("first column cannot be horizontally spanned");
  1341.     return;
  1342.   }
  1343.   table_entry *e = entry[r][c];
  1344.   if (e) {
  1345.     assert(e->start_row <= r && r <= e->end_row
  1346.        && e->start_col <= c && c <= e->end_col
  1347.        && e->end_row - e->start_row > 0
  1348.        && e->end_col - e->start_col > 0);
  1349.     return;
  1350.   }
  1351.   e = entry[r][c-1];
  1352.   // e can be 0 if we had an empty entry or an error
  1353.   if (e == 0)
  1354.     return;
  1355.   if (e->start_row != r) {
  1356.     /*
  1357.       l l
  1358.       ^ s */
  1359.     error("impossible horizontal span at row %1, column %2", r + 1, c + 1);
  1360.   }
  1361.   else {
  1362.     e->end_col = c;
  1363.     entry[r][c] = e;
  1364.   }
  1365. }
  1366.  
  1367. void table::do_vspan(int r, int c)
  1368. {
  1369.   assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns);
  1370.   if (r == 0) {
  1371.     error("first row cannot be vertically spanned");
  1372.     return;
  1373.   }
  1374.   table_entry *e = entry[r][c];
  1375.   if (e) {
  1376.     assert(e->start_row <= r && r <= e->end_row
  1377.        && e->start_col <= c && c <= e->end_col
  1378.        && e->end_row - e->start_row > 0
  1379.        && e->end_col - e->start_col > 0);
  1380.     return;
  1381.   }
  1382.   e = entry[r-1][c];
  1383.   // e can be 0 if we had an empty entry or an error
  1384.   if (e == 0)
  1385.     return;
  1386.   if (e->start_col != c) {
  1387.     /* l s
  1388.        l ^ */
  1389.     error("impossible vertical span at row %1, column %2", r + 1, c + 1);
  1390.   }
  1391.   else {
  1392.     for (int i = c; i <= e->end_col; i++) {
  1393.       assert(entry[r][i] == 0);
  1394.       entry[r][i] = e;
  1395.     }
  1396.     e->end_row = r;
  1397.   }
  1398. }
  1399.  
  1400. int find_dot(const char *s, const char *delim)
  1401. {
  1402.   if (s == 0 || *s == '\0')
  1403.     return -1;
  1404.   const char *p;
  1405.   int in_delim = 0;        // is p within eqn delimiters?
  1406.   // tbl recognises \& even within eqn delimiters; I don't
  1407.   for (p = s; *p; p++)
  1408.     if (in_delim) {
  1409.       if (*p == delim[1])
  1410.     in_delim = 0;
  1411.     }
  1412.     else if (*p == delim[0])
  1413.       in_delim = 1;
  1414.     else if (p[0] == '\\' && p[1] == '&')
  1415.       return p - s;
  1416.   int possible_pos = -1;
  1417.   in_delim = 0;
  1418.   for (p = s; *p; p++)
  1419.     if (in_delim) {
  1420.       if (*p == delim[1])
  1421.     in_delim = 0;
  1422.     }
  1423.     else if (*p == delim[0])
  1424.       in_delim = 1;
  1425.     else if (p[0] == '.' && csdigit(p[1]))
  1426.       possible_pos = p - s;
  1427.   if (possible_pos >= 0)
  1428.     return possible_pos;
  1429.   in_delim = 0;
  1430.   for (p = s; *p; p++)
  1431.     if (in_delim) {
  1432.       if (*p == delim[1])
  1433.     in_delim = 0;
  1434.     }
  1435.     else if (*p == delim[0])
  1436.       in_delim = 1;
  1437.     else if (csdigit(*p))
  1438.       possible_pos = p + 1 - s;
  1439.   return possible_pos;
  1440. }
  1441.  
  1442. void table::add_entry(int r, int c, const string &str, const entry_format *f,
  1443.               const char *fn, int ln)
  1444. {
  1445.   allocate(r);
  1446.   table_entry *e = 0;
  1447.   if (str == "\\_") {
  1448.     e = new short_line_entry(f);
  1449.   }
  1450.   else if (str == "\\=") {
  1451.     e = new short_double_line_entry(f);
  1452.   }
  1453.   else if (str == "_") {
  1454.     single_line_entry *lefte;
  1455.     if (c > 0 && entry[r][c-1] != 0 &&
  1456.     (lefte = entry[r][c-1]->to_single_line_entry()) != 0
  1457.     && lefte->start_row == r
  1458.     && lefte->mod->stagger == f->stagger) {
  1459.       lefte->end_col = c;
  1460.       entry[r][c] = lefte;
  1461.     }
  1462.     else
  1463.       e = new single_line_entry(f);
  1464.   }
  1465.   else if (str == "=") {
  1466.     double_line_entry *lefte;
  1467.     if (c > 0 && entry[r][c-1] != 0 &&
  1468.     (lefte = entry[r][c-1]->to_double_line_entry()) != 0
  1469.     && lefte->start_row == r
  1470.     && lefte->mod->stagger == f->stagger) {
  1471.       lefte->end_col = c;
  1472.       entry[r][c] = lefte;
  1473.     }
  1474.     else
  1475.       e = new double_line_entry(f);
  1476.   }
  1477.   else if (str == "\\^") {
  1478.     do_vspan(r, c);
  1479.   }
  1480.   else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') {
  1481.     if (str.search('\n') >= 0)
  1482.       error_with_file_and_line(fn, ln, "bad repeated character");
  1483.     else {
  1484.       char *s = str.substring(2, str.length() - 2).extract();
  1485.       e = new repeated_char_entry(s, f);
  1486.     }
  1487.   }
  1488.   else {
  1489.     int is_block = str.search('\n') >= 0;
  1490.     char *s;
  1491.     switch (f->type) {
  1492.     case FORMAT_SPAN:
  1493.       assert(str.empty());
  1494.       do_hspan(r, c);
  1495.       break;
  1496.     case FORMAT_LEFT:
  1497.       if (!str.empty()) {
  1498.     s = str.extract();
  1499.     if (is_block)
  1500.       e = new left_block_entry(s, f);
  1501.     else
  1502.       e = new left_text_entry(s, f);
  1503.       }
  1504.       else
  1505.     e = new empty_entry(f);
  1506.       break;
  1507.     case FORMAT_CENTER:
  1508.       if (!str.empty()) {
  1509.     s = str.extract();
  1510.     if (is_block)
  1511.       e = new center_block_entry(s, f);
  1512.     else
  1513.       e = new center_text_entry(s, f);
  1514.       }
  1515.       else
  1516.     e = new empty_entry(f);
  1517.       break;
  1518.     case FORMAT_RIGHT:
  1519.       if (!str.empty()) {
  1520.     s = str.extract();
  1521.     if (is_block)
  1522.       e = new right_block_entry(s, f);
  1523.     else
  1524.       e = new right_text_entry(s, f);
  1525.       }
  1526.       else
  1527.     e = new empty_entry(f);
  1528.       break;
  1529.     case FORMAT_NUMERIC:
  1530.       if (!str.empty()) {
  1531.     s = str.extract();
  1532.     if (is_block) {
  1533.       error_with_file_and_line(fn, ln, "can't have numeric text block");
  1534.       e = new left_block_entry(s, f);
  1535.     }
  1536.     else {
  1537.       int pos = find_dot(s, delim);
  1538.       if (pos < 0)
  1539.         e = new center_text_entry(s, f);
  1540.       else
  1541.         e = new numeric_text_entry(s, f, pos);
  1542.     }
  1543.       }
  1544.       else
  1545.     e = new empty_entry(f);
  1546.       break;
  1547.     case FORMAT_ALPHABETIC:
  1548.       if (!str.empty()) {
  1549.     s = str.extract();
  1550.     if (is_block)
  1551.       e = new alphabetic_block_entry(s, f);
  1552.     else
  1553.       e = new alphabetic_text_entry(s, f);
  1554.       }
  1555.       else
  1556.     e = new empty_entry(f);
  1557.       break;
  1558.     case FORMAT_VSPAN:
  1559.       do_vspan(r, c);
  1560.       break;
  1561.     case FORMAT_HLINE:
  1562.       if (str.length() != 0)
  1563.     error_with_file_and_line(fn, ln,
  1564.                  "non-empty data entry for `_' format ignored");
  1565.       e = new single_line_entry(f);
  1566.       break;
  1567.     case FORMAT_DOUBLE_HLINE:
  1568.       if (str.length() != 0)
  1569.     error_with_file_and_line(fn, ln,
  1570.                  "non-empty data entry for `=' format ignored");
  1571.       e = new double_line_entry(f);
  1572.       break;
  1573.     default:
  1574.       assert(0);
  1575.     }
  1576.   }
  1577.   if (e) {
  1578.     table_entry *preve = entry[r][c];
  1579.     if (preve) {
  1580.       /* c s
  1581.          ^ l */
  1582.       error_with_file_and_line(fn, ln, "row %1, column %2 already spanned",
  1583.                    r + 1, c + 1);
  1584.       delete e;
  1585.     }
  1586.     else {
  1587.       e->input_lineno = ln;
  1588.       e->input_filename = fn;
  1589.       e->start_row = e->end_row = r;
  1590.       e->start_col = e->end_col = c;
  1591.       for (table_entry **p = &entry_list; *p; p = &(*p)->next)
  1592.     ;
  1593.       *p = e;
  1594.       entry[r][c] = e;
  1595.     }
  1596.   }
  1597. }
  1598.  
  1599. // add vertical lines for row r
  1600.  
  1601. void table::add_vlines(int r, const char *v)
  1602. {
  1603.   allocate(r);
  1604.   for (int i = 0; i < ncolumns+1; i++)
  1605.     vline[r][i] = v[i];
  1606. }
  1607.  
  1608. void table::check()
  1609. {
  1610.   table_entry *p = entry_list;
  1611.   int i, j;
  1612.   while (p) {
  1613.     for (i = p->start_row; i <= p->end_row; i++)
  1614.       for (j = p->start_col; j <= p->end_col; j++)
  1615.     assert(entry[i][j] == p);
  1616.     p = p->next;
  1617.   }
  1618. }
  1619.  
  1620. void table::print()
  1621. {
  1622.   location_force_filename = 1;
  1623.   check();
  1624.   init_output();
  1625.   determine_row_type();
  1626.   compute_widths();
  1627.   if (!(flags & CENTER))
  1628.     prints(".if \\n[" SAVED_CENTER_REG "] \\{");
  1629.   prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2)\n"
  1630.      ".nr " SAVED_INDENT_REG " \\n[.i]\n");
  1631.   if (!(flags & CENTER))
  1632.     prints(".\\}\n");
  1633.   build_vrule_list();
  1634.   define_bottom_macro();
  1635.   do_top();
  1636.   for (int i = 0; i < nrows; i++)
  1637.     do_row(i);
  1638.   do_bottom();
  1639. }
  1640.  
  1641. void table::determine_row_type()
  1642. {
  1643.   row_is_all_lines = new char[nrows];
  1644.   for (int i = 0; i < nrows; i++) {
  1645.     int had_single = 0;
  1646.     int had_double = 0;
  1647.     int had_non_line = 0;
  1648.     for (int c = 0; c < ncolumns; c++) {
  1649.       table_entry *e = entry[i][c];
  1650.       if (e != 0) {
  1651.     if (e->start_row == e->end_row) {
  1652.       int t = e->line_type();
  1653.       switch (t) {
  1654.       case -1:
  1655.         had_non_line = 1;
  1656.         break;
  1657.       case 0:
  1658.         // empty
  1659.         break;
  1660.       case 1:
  1661.         had_single = 1;
  1662.         break;
  1663.       case 2:
  1664.         had_double = 1;
  1665.         break;
  1666.       default:
  1667.         assert(0);
  1668.       }
  1669.       if (had_non_line)
  1670.         break;
  1671.     }
  1672.     c = e->end_col;
  1673.       }
  1674.     }
  1675.     if (had_non_line)
  1676.       row_is_all_lines[i] = 0;
  1677.     else if (had_double)
  1678.       row_is_all_lines[i] = 2;
  1679.     else if (had_single)
  1680.       row_is_all_lines[i] = 1;
  1681.     else
  1682.       row_is_all_lines[i] = 0;
  1683.   }
  1684. }
  1685.  
  1686.  
  1687. void table::init_output()
  1688. {
  1689.   prints(".nr " COMPATIBLE_REG " \\n(.C\n"
  1690.      ".cp 0\n");
  1691.   if (linesize > 0)
  1692.     printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize));
  1693.   else
  1694.     prints(".nr " LINESIZE_REG " \\n[.s]\n");
  1695.   if (!(flags & CENTER))
  1696.     prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n");
  1697.   prints(".de " RESET_MACRO_NAME "\n"
  1698.      ".ft \\n[.f]\n"
  1699.      ".ps \\n[.s]\n"
  1700.      ".vs \\n[.v]u\n"
  1701.      ".in \\n[.i]u\n"
  1702.      ".ll \\n[.l]u\n"
  1703.      ".ls \\n[.L]\n"
  1704.      ".ad \\n[.j]\n"
  1705.      ".ie \\n[.u] .fi\n"
  1706.      ".el .nf\n"
  1707.      ".ce \\n[.ce]\n"
  1708.      "..\n"
  1709.      ".nr " SAVED_INDENT_REG " \\n[.i]\n"
  1710.      ".nr " SAVED_FONT_REG " \\n[.f]\n"
  1711.      ".nr " SAVED_SIZE_REG " \\n[.s]\n"
  1712.      ".nr " SAVED_FILL_REG " \\n[.u]\n"
  1713.      ".nr T. 0\n"
  1714.      ".nr " CURRENT_ROW_REG " 0-1\n"
  1715.      ".nr " LAST_PASSED_ROW_REG " 0-1\n"
  1716.      ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
  1717.      ".ds " TRANSPARENT_STRING_NAME "\n"
  1718.      ".ds " QUOTE_STRING_NAME "\n"
  1719.      ".nr " NEED_BOTTOM_RULE_REG " 1\n"
  1720.      ".nr " SUPPRESS_BOTTOM_REG " 0\n"
  1721.      ".eo\n"
  1722.      ".de " REPEATED_MARK_MACRO "\n"
  1723.      ".mk \\$1\n"
  1724.      ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n"
  1725.      "..\n"
  1726.      ".de " REPEATED_VPT_MACRO "\n"
  1727.      ".vpt \\$1\n"
  1728.      ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
  1729.      "..\n");
  1730.   if (!(flags & NOKEEP))
  1731.     prints(".de " KEEP_MACRO_NAME "\n"
  1732.        ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n"
  1733.        ".ds " TRANSPARENT_STRING_NAME " \\!\n"
  1734.        ".di " SECTION_DIVERSION_NAME "\n"
  1735.        ".nr " SECTION_DIVERSION_FLAG_REG " 1\n"
  1736.        ".in 0\n"
  1737.        ".\\}\n"
  1738.        "..\n"
  1739.        ".de " RELEASE_MACRO_NAME "\n"
  1740.        ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{"
  1741.        ".di\n"
  1742.        ".in \\n[" SAVED_INDENT_REG "]u\n"
  1743.        ".nr " SAVED_DN_REG " \\n[dn]\n"
  1744.        ".ds " QUOTE_STRING_NAME "\n"
  1745.        ".ds " TRANSPARENT_STRING_NAME "\n"
  1746.        ".nr " SECTION_DIVERSION_FLAG_REG " 0\n"
  1747.        ".if \\n[.t]<=\\n[dn] \\{"
  1748.        ".nr T. 1\n"
  1749.        ".T#\n"
  1750.        ".nr " SUPPRESS_BOTTOM_REG " 1\n"
  1751.        ".sp \\n[.t]u\n"
  1752.        ".nr " SUPPRESS_BOTTOM_REG " 0\n"
  1753.        ".mk #T\n"
  1754.        ".\\}\n"
  1755.        ".if \\n[.t]<=\\n[" SAVED_DN_REG "] "
  1756.        /* Since we turn off traps, it won't get into an infinite loop
  1757.        when we try and print it; it will just go off the bottom of the
  1758.        page. */
  1759.        ".tm warning: page \\n%: table text block will not fit on one page\n"
  1760.        ".nf\n"
  1761.        ".ls 1\n"
  1762.        "." SECTION_DIVERSION_NAME "\n"
  1763.        ".ls\n"
  1764.        ".rm " SECTION_DIVERSION_NAME "\n"
  1765.        ".\\}\n"
  1766.        "..\n"
  1767.        ".nr " TABLE_DIVERSION_FLAG_REG " 0\n"
  1768.        ".de " TABLE_KEEP_MACRO_NAME "\n"
  1769.        ".if '\\n[.z]'' \\{"
  1770.        ".di " TABLE_DIVERSION_NAME "\n"
  1771.        ".nr " TABLE_DIVERSION_FLAG_REG " 1\n"
  1772.        ".\\}\n"
  1773.        "..\n"
  1774.        ".de " TABLE_RELEASE_MACRO_NAME "\n"
  1775.        ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n"
  1776.        ".di\n"
  1777.        ".nr " SAVED_DN_REG " \\n[dn]\n"
  1778.        ".ne \\n[dn]u+\\n[.V]u\n"
  1779.        ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] "
  1780.        ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n"
  1781.        ".el \\{"
  1782.        ".in 0\n"
  1783.        ".ls 1\n"
  1784.        ".nf\n"
  1785.        "." TABLE_DIVERSION_NAME "\n"
  1786.        ".\\}\n"
  1787.        ".rm " TABLE_DIVERSION_NAME "\n"
  1788.        ".\\}\n"
  1789.        "..\n");
  1790.   prints(".ec\n"
  1791.      ".ce 0\n"
  1792.      ".nf\n");
  1793. }
  1794.  
  1795. string block_width_reg(int r, int c)
  1796. {
  1797.   static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
  1798.   sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c);
  1799.   return string(name);
  1800. }
  1801.  
  1802. string block_diversion_name(int r, int c)
  1803. {
  1804.   static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS];
  1805.   sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c);
  1806.   return string(name);
  1807. }
  1808.  
  1809. string block_height_reg(int r, int c)
  1810. {
  1811.   static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS];
  1812.   sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c);
  1813.   return string(name);
  1814. }
  1815.  
  1816. string span_width_reg(int start_col, int end_col)
  1817. {
  1818.   static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
  1819.   sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col);
  1820.   if (end_col != start_col)
  1821.     sprintf(strchr(name, '\0'), ",%d", end_col);
  1822.   return string(name);
  1823. }
  1824.  
  1825. string span_left_numeric_width_reg(int start_col, int end_col)
  1826. {
  1827.   static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
  1828.   sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col);
  1829.   if (end_col != start_col)
  1830.     sprintf(strchr(name, '\0'), ",%d", end_col);
  1831.   return string(name);
  1832. }
  1833.  
  1834. string span_right_numeric_width_reg(int start_col, int end_col)
  1835. {
  1836.   static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
  1837.   sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col);
  1838.   if (end_col != start_col)
  1839.     sprintf(strchr(name, '\0'), ",%d", end_col);
  1840.   return string(name);
  1841. }
  1842.  
  1843. string span_alphabetic_width_reg(int start_col, int end_col)
  1844. {
  1845.   static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS];
  1846.   sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col);
  1847.   if (end_col != start_col)
  1848.     sprintf(strchr(name, '\0'), ",%d", end_col);
  1849.   return string(name);
  1850. }
  1851.  
  1852.  
  1853. string column_separation_reg(int col)
  1854. {
  1855.   static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS];
  1856.   sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col);
  1857.   return string(name);
  1858. }
  1859.  
  1860. string row_start_reg(int row)
  1861. {
  1862.   static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS];
  1863.   sprintf(name, ROW_START_PREFIX "%d", row);
  1864.   return string(name);
  1865. }  
  1866.  
  1867. string column_start_reg(int col)
  1868. {
  1869.   static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS];
  1870.   sprintf(name, COLUMN_START_PREFIX "%d", col);
  1871.   return string(name);
  1872. }  
  1873.  
  1874. string column_end_reg(int col)
  1875. {
  1876.   static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS];
  1877.   sprintf(name, COLUMN_END_PREFIX "%d", col);
  1878.   return string(name);
  1879. }
  1880.  
  1881. string column_divide_reg(int col)
  1882. {
  1883.   static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS];
  1884.   sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col);
  1885.   return string(name);
  1886. }
  1887.  
  1888. string row_top_reg(int row)
  1889. {
  1890.   static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS];
  1891.   sprintf(name, ROW_TOP_PREFIX "%d", row);
  1892.   return string(name);
  1893. }
  1894.  
  1895. void init_span_reg(int start_col, int end_col)
  1896. {
  1897.   printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n",
  1898.       span_width_reg(start_col, end_col),
  1899.       span_alphabetic_width_reg(start_col, end_col),
  1900.       span_left_numeric_width_reg(start_col, end_col),
  1901.       span_right_numeric_width_reg(start_col, end_col));
  1902. }
  1903.  
  1904. void compute_span_width(int start_col, int end_col)
  1905. {
  1906.   printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n"
  1907.       ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n", 
  1908.       span_width_reg(start_col, end_col),
  1909.       span_left_numeric_width_reg(start_col, end_col),
  1910.       span_right_numeric_width_reg(start_col, end_col),
  1911.       span_alphabetic_width_reg(start_col, end_col));
  1912.      
  1913. }
  1914.  
  1915. // Increase the widths of columns so that the width of any spanning entry
  1916. // is no greater than the sum of the widths of the columns that it spans.
  1917. // Ensure that the widths of columns remain equal.
  1918.  
  1919. void table::divide_span(int start_col, int end_col)
  1920. {
  1921.   assert(end_col > start_col);
  1922.   printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]", 
  1923.       span_width_reg(start_col, end_col),
  1924.       span_width_reg(start_col, start_col));
  1925.   for (int i = start_col + 1; i <= end_col; i++) {
  1926.     // The column separation may shrink with the expand option.
  1927.     if (!(flags & EXPAND))
  1928.       printfs("+%1n", as_string(column_separation[i - 1]));
  1929.     printfs("+\\n[%1]", span_width_reg(i, i));
  1930.   }
  1931.   prints(")\n");
  1932.   printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n",
  1933.       as_string(end_col - start_col + 1));
  1934.   prints(".if \\n[" NEEDED_REG "] \\{");
  1935.   for (i = start_col; i <= end_col; i++)
  1936.     printfs(".nr %1 +\\n[" NEEDED_REG "]\n", 
  1937.         span_width_reg(i, i));
  1938.   int equal_flag = 0;
  1939.   for (i = start_col; i <= end_col && !equal_flag; i++)
  1940.     if (equal[i])
  1941.       equal_flag = 1;
  1942.   if (equal_flag) {
  1943.     for (i = 0; i < ncolumns; i++)
  1944.       if (i < start_col || i > end_col)
  1945.     printfs(".nr %1 +\\n[" NEEDED_REG "]\n", 
  1946.         span_width_reg(i, i));
  1947.   }
  1948.   prints(".\\}\n");
  1949. }
  1950.  
  1951.  
  1952. void table::sum_columns(int start_col, int end_col)
  1953. {
  1954.   assert(end_col > start_col);
  1955.   printfs(".nr %1 \\n[%2]", 
  1956.       span_width_reg(start_col, end_col),
  1957.       span_width_reg(start_col, start_col));
  1958.   for (int i = start_col + 1; i <= end_col; i++)
  1959.     printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]",
  1960.         as_string(column_separation[i - 1]),
  1961.         span_width_reg(i, i));
  1962.   prints('\n');
  1963. }
  1964.  
  1965. horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p)
  1966. : start_col(sc), end_col(ec), next(p)
  1967. {
  1968. }
  1969.  
  1970. void table::build_span_list()
  1971. {
  1972.   span_list = 0;
  1973.   table_entry *p = entry_list;
  1974.   while (p) {
  1975.     if (p->end_col != p->start_col) {
  1976.       for (horizontal_span *q = span_list; q; q = q->next)
  1977.     if (q->start_col == p->start_col
  1978.         && q->end_col == p->end_col)
  1979.       break;
  1980.       if (!q)
  1981.     span_list = new horizontal_span(p->start_col, p->end_col, span_list);
  1982.     }
  1983.     p = p->next;
  1984.   }
  1985.   // Now sort span_list primarily by order of end_row, and secondarily
  1986.   // by reverse order of start_row. This ensures that if we divide
  1987.   // spans using the order in span_list, we will get reasonable results.
  1988.   horizontal_span *unsorted = span_list;
  1989.   span_list = 0;
  1990.   while (unsorted) {
  1991.     for (horizontal_span **pp = &span_list; *pp; pp = &(*pp)->next)
  1992.       if (unsorted->end_col < (*pp)->end_col
  1993.       || (unsorted->end_col == (*pp)->end_col
  1994.           && (unsorted->start_col > (*pp)->start_col)))
  1995.     break;
  1996.     horizontal_span *tem = unsorted->next;
  1997.     unsorted->next = *pp;
  1998.     *pp = unsorted;
  1999.     unsorted = tem;
  2000.   }
  2001. }
  2002.  
  2003.  
  2004. void table::compute_separation_factor()
  2005. {
  2006.   if (flags & (ALLBOX|BOX|DOUBLEBOX))
  2007.     left_separation = right_separation = 1;
  2008.   else {
  2009.     for (int i = 0; i < nrows; i++) {
  2010.       if (vline[i][0] > 0)
  2011.     left_separation = 1;
  2012.       if (vline[i][ncolumns] > 0)
  2013.     right_separation = 1;
  2014.     }
  2015.   }
  2016.   if (flags & EXPAND) {
  2017.     int total_sep = left_separation + right_separation;
  2018.     for (int i = 0; i < ncolumns - 1; i++)
  2019.       total_sep += column_separation[i];
  2020.     if (total_sep != 0) {
  2021.       // Don't let the separation factor be negative.
  2022.       prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]");
  2023.       for (i = 0; i < ncolumns; i++)
  2024.     printfs("-\\n[%1]", span_width_reg(i, i));
  2025.       printfs("/%1>?0\n", as_string(total_sep));
  2026.     }
  2027.   }
  2028. }
  2029.  
  2030. void table::compute_column_positions()
  2031. {
  2032.   printfs(".nr %1 0\n", column_divide_reg(0));
  2033.   printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n",
  2034.       column_start_reg(0),
  2035.       as_string(left_separation));
  2036.   for (int i = 1;; i++) {
  2037.     printfs(".nr %1 \\n[%2]+\\n[%3]\n",
  2038.         column_end_reg(i-1),
  2039.         column_start_reg(i-1),
  2040.         span_width_reg(i-1, i-1));
  2041.     if (i >= ncolumns)
  2042.       break;
  2043.     printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
  2044.         column_start_reg(i),
  2045.         column_end_reg(i-1),
  2046.         as_string(column_separation[i-1]));
  2047.     printfs(".nr %1 \\n[%2]+\\n[%3]/2\n",
  2048.         column_divide_reg(i),
  2049.         column_end_reg(i-1),
  2050.         column_start_reg(i));
  2051.   }
  2052.   printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n",
  2053.       column_divide_reg(ncolumns),
  2054.       column_end_reg(i-1),
  2055.       as_string(right_separation));
  2056.   printfs(".nr TW \\n[%1]\n",
  2057.       column_divide_reg(ncolumns));
  2058.   if (flags & DOUBLEBOX) {
  2059.     printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0));
  2060.     printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns));
  2061.   }
  2062. }
  2063.  
  2064. void table::make_columns_equal()
  2065. {
  2066.   int first = -1;        // index of first equal column
  2067.   for (int i = 0; i < ncolumns; i++)
  2068.     if (equal[i]) {
  2069.       if (first < 0) {
  2070.     printfs(".nr %1 \\n[%1]", span_width_reg(i, i));
  2071.     first = i;
  2072.       }
  2073.       else
  2074.     printfs(">?\\n[%1]", span_width_reg(i, i));
  2075.     }
  2076.   if (first >= 0) {
  2077.     prints('\n');
  2078.     for (i = first + 1; i < ncolumns; i++)
  2079.       if (equal[i])
  2080.     printfs(".nr %1 \\n[%2]\n", 
  2081.         span_width_reg(i, i),
  2082.         span_width_reg(first, first));
  2083.   }
  2084. }
  2085.  
  2086. void table::compute_widths()
  2087. {
  2088.   build_span_list();
  2089.   int i;
  2090.   horizontal_span *p;
  2091.   prints(".nr " SEPARATION_FACTOR_REG " 1n\n");
  2092.   for (i = 0; i < ncolumns; i++) {
  2093.     init_span_reg(i, i);
  2094.     if (!minimum_width[i].empty())
  2095.       printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]);
  2096.   }
  2097.   for (p = span_list; p; p = p->next)
  2098.     init_span_reg(p->start_col, p->end_col);
  2099.   table_entry *q;
  2100.   for (q = entry_list; q; q = q->next)
  2101.     if (!q->mod->zero_width)
  2102.       q->do_width();
  2103.   for (i = 0; i < ncolumns; i++)
  2104.     compute_span_width(i, i);
  2105.   for (p = span_list; p; p = p->next)
  2106.     compute_span_width(p->start_col, p->end_col);
  2107.   make_columns_equal();
  2108.   // Note that divide_span keeps equal width columns equal.
  2109.   for (p = span_list; p; p = p->next)
  2110.     divide_span(p->start_col, p->end_col);
  2111.   for (p = span_list; p; p = p->next)
  2112.     sum_columns(p->start_col, p->end_col);
  2113.   int had_spanning_block = 0;
  2114.   int had_equal_block = 0;
  2115.   for (q = entry_list; q; q = q->next)
  2116.     if (q->divert(ncolumns, minimum_width,
  2117.           (flags & EXPAND) ? column_separation : 0)) {
  2118.       if (q->end_col > q->start_col)
  2119.     had_spanning_block = 1;
  2120.       for (i = q->start_col; i <= q->end_col && !had_equal_block; i++)
  2121.     if (equal[i])
  2122.       had_equal_block = 1;
  2123.     }
  2124.   if (had_equal_block)
  2125.     make_columns_equal();
  2126.   if (had_spanning_block)
  2127.     for (p = span_list; p; p = p->next)
  2128.       divide_span(p->start_col, p->end_col);
  2129.   compute_separation_factor();
  2130.   for (p = span_list; p; p = p->next)
  2131.     sum_columns(p->start_col, p->end_col);
  2132.   compute_column_positions();
  2133. }
  2134.  
  2135. void table::print_single_hline(int r)
  2136. {
  2137.   prints(".vs " LINE_SEP ">?\\n[.V]u\n"
  2138.      ".ls 1\n"
  2139.      "\\v'" BODY_DEPTH "'"
  2140.      "\\s[\\n[" LINESIZE_REG "]]");
  2141.   if (r > nrows - 1)
  2142.     prints("\\D'l |\\n[TW]u 0'");
  2143.   else {
  2144.     int start_col = 0;
  2145.     for (;;) {
  2146.       while (start_col < ncolumns 
  2147.          && entry[r][start_col] != 0
  2148.          && entry[r][start_col]->start_row != r)
  2149.     start_col++;
  2150.       for (int end_col = start_col;
  2151.        end_col < ncolumns
  2152.        && (entry[r][end_col] == 0
  2153.            || entry[r][end_col]->start_row == r);
  2154.        end_col++)
  2155.     ;
  2156.       if (end_col <= start_col)
  2157.     break;
  2158.       printfs("\\h'|\\n[%1]u",
  2159.           column_divide_reg(start_col));
  2160.       if ((r > 0 && vline[r-1][start_col] == 2)
  2161.       || (r < nrows && vline[r][start_col] == 2))
  2162.     prints("-" HALF_DOUBLE_LINE_SEP);
  2163.       prints("'");
  2164.       printfs("\\D'l |\\n[%1]u",
  2165.           column_divide_reg(end_col));
  2166.       if ((r > 0 && vline[r-1][end_col] == 2)
  2167.       || (r < nrows && vline[r][end_col] == 2))
  2168.     prints("+" HALF_DOUBLE_LINE_SEP);
  2169.       prints(" 0'");
  2170.       start_col = end_col;
  2171.     }
  2172.   }
  2173.   prints("\\s0\n");
  2174.   prints(".ls\n"
  2175.      ".vs\n");
  2176. }
  2177.  
  2178. void table::print_double_hline(int r)
  2179. {
  2180.   prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
  2181.      ">?\\n[.V]u\n"
  2182.      ".ls 1\n"
  2183.      "\\v'" BODY_DEPTH "'"
  2184.      "\\s[\\n[" LINESIZE_REG "]]");
  2185.   if (r > nrows - 1)
  2186.     prints("\\v'-" DOUBLE_LINE_SEP "'"
  2187.        "\\D'l |\\n[TW]u 0'"
  2188.        "\\v'" DOUBLE_LINE_SEP "'"
  2189.        "\\h'|0'"
  2190.        "\\D'l |\\n[TW]u 0'");
  2191.   else {
  2192.     int start_col = 0;
  2193.     for (;;) {
  2194.       while (start_col < ncolumns 
  2195.          && entry[r][start_col] != 0
  2196.          && entry[r][start_col]->start_row != r)
  2197.     start_col++;
  2198.       for (int end_col = start_col;
  2199.        end_col < ncolumns
  2200.        && (entry[r][end_col] == 0
  2201.            || entry[r][end_col]->start_row == r);
  2202.        end_col++)
  2203.     ;
  2204.       if (end_col <= start_col)
  2205.     break;
  2206.       const char *left_adjust = 0;
  2207.       if ((r > 0 && vline[r-1][start_col] == 2)
  2208.       || (r < nrows && vline[r][start_col] == 2))
  2209.     left_adjust = "-" HALF_DOUBLE_LINE_SEP;
  2210.       const char *right_adjust = 0;
  2211.       if ((r > 0 && vline[r-1][end_col] == 2)
  2212.       || (r < nrows && vline[r][end_col] == 2))
  2213.     right_adjust = "+" HALF_DOUBLE_LINE_SEP;
  2214.       printfs("\\v'-" DOUBLE_LINE_SEP "'"
  2215.           "\\h'|\\n[%1]u",
  2216.           column_divide_reg(start_col));
  2217.       if (left_adjust)
  2218.     prints(left_adjust);
  2219.       prints("'");
  2220.       printfs("\\D'l |\\n[%1]u",
  2221.           column_divide_reg(end_col));
  2222.       if (right_adjust)
  2223.     prints(right_adjust);
  2224.       prints(" 0'");
  2225.       printfs("\\v'" DOUBLE_LINE_SEP "'"
  2226.           "\\h'|\\n[%1]u",
  2227.           column_divide_reg(start_col));
  2228.       if (left_adjust)
  2229.     prints(left_adjust);
  2230.       prints("'");
  2231.       printfs("\\D'l |\\n[%1]u",
  2232.           column_divide_reg(end_col));
  2233.       if (right_adjust)
  2234.     prints(right_adjust);
  2235.       prints(" 0'");
  2236.       start_col = end_col;
  2237.     }
  2238.   }
  2239.   prints("\\s0\n"
  2240.      ".ls\n"
  2241.      ".vs\n");
  2242. }
  2243.  
  2244. void table::compute_vrule_top_adjust(int start_row, int col, string &result)
  2245. {
  2246.   if (row_is_all_lines[start_row] && start_row < nrows - 1) {
  2247.     if (row_is_all_lines[start_row] == 2)
  2248.       result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP;
  2249.     else
  2250.       result = LINE_SEP ">?\\n[.V]u";
  2251.     start_row++;
  2252.   }
  2253.   else {
  2254.     result = "";
  2255.     if (start_row == 0)
  2256.       return;
  2257.     for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next)
  2258.       if (p->row == start_row 
  2259.       && (p->is_single_line() || p->is_double_line()))
  2260.     return;
  2261.   }
  2262.   int left = 0;
  2263.   if (col > 0) {
  2264.     table_entry *e = entry[start_row-1][col-1];
  2265.     if (e && e->start_row == e->end_row) {
  2266.       if (e->to_double_line_entry() != 0)
  2267.     left = 2;
  2268.       else if (e->to_single_line_entry() != 0)
  2269.     left = 1;
  2270.     }
  2271.   }
  2272.   int right = 0;
  2273.   if (col < ncolumns) {
  2274.     table_entry *e = entry[start_row-1][col];
  2275.     if (e && e->start_row == e->end_row) {
  2276.       if (e->to_double_line_entry() != 0)
  2277.     right = 2;
  2278.       else if (e->to_single_line_entry() != 0)
  2279.     right = 1;
  2280.     }
  2281.   }
  2282.   if (row_is_all_lines[start_row-1] == 0) {
  2283.     if (left > 0 || right > 0) {
  2284.       result += "-" BODY_DEPTH "-" BAR_HEIGHT;
  2285.       if ((left == 2 && right != 2) || (right == 2 && left != 2))
  2286.     result += "-" HALF_DOUBLE_LINE_SEP;
  2287.       else if (left == 2 && right == 2)
  2288.     result += "+" HALF_DOUBLE_LINE_SEP;
  2289.     }
  2290.   }
  2291.   else if (row_is_all_lines[start_row-1] == 2) {
  2292.     if ((left == 2 && right != 2) || (right == 2 && left != 2))
  2293.       result += "-" DOUBLE_LINE_SEP;
  2294.     else if (left == 1 || right == 1)
  2295.       result += "-" HALF_DOUBLE_LINE_SEP;
  2296.   }
  2297. }
  2298.  
  2299. void table::compute_vrule_bot_adjust(int end_row, int col, string &result)
  2300. {
  2301.   if (row_is_all_lines[end_row] && end_row > 0) {
  2302.     end_row--;
  2303.     result = "";
  2304.   }
  2305.   else {
  2306.     for (stuff *p = stuff_list; p && p->row < end_row + 1; p = p->next)
  2307.       ;
  2308.     if (p && p->row == end_row + 1 && p->is_double_line()) {
  2309.       result = "-" DOUBLE_LINE_SEP;
  2310.       return;
  2311.     }
  2312.     if ((p != 0 && p->row == end_row + 1)
  2313.     || end_row == nrows - 1) {
  2314.       result = "";
  2315.       return;
  2316.     }
  2317.     if (row_is_all_lines[end_row+1] == 1)
  2318.       result = LINE_SEP;
  2319.     else if (row_is_all_lines[end_row+1] == 2)
  2320.       result = LINE_SEP "+" DOUBLE_LINE_SEP;
  2321.     else
  2322.       result = "";
  2323.   }
  2324.   int left = 0;
  2325.   if (col > 0) {
  2326.     table_entry *e = entry[end_row+1][col-1];
  2327.     if (e && e->start_row == e->end_row) {
  2328.       if (e->to_double_line_entry() != 0)
  2329.     left = 2;
  2330.       else if (e->to_single_line_entry() != 0)
  2331.     left = 1;
  2332.     }
  2333.   }
  2334.   int right = 0;
  2335.   if (col < ncolumns) {
  2336.     table_entry *e = entry[end_row+1][col];
  2337.     if (e && e->start_row == e->end_row) {
  2338.       if (e->to_double_line_entry() != 0)
  2339.     right = 2;
  2340.       else if (e->to_single_line_entry() != 0)
  2341.     right = 1;
  2342.     }
  2343.   }
  2344.   if (row_is_all_lines[end_row+1] == 0) {
  2345.     if (left > 0 || right > 0) {
  2346.       result = "1v-" BODY_DEPTH "-" BAR_HEIGHT;
  2347.       if ((left == 2 && right != 2) || (right == 2 && left != 2))
  2348.     result += "+" HALF_DOUBLE_LINE_SEP;
  2349.       else if (left == 2 && right == 2)
  2350.     result += "-" HALF_DOUBLE_LINE_SEP;
  2351.     }
  2352.   }
  2353.   else if (row_is_all_lines[end_row+1] == 2) {
  2354.     if (left == 2 && right == 2)
  2355.       result += "-" DOUBLE_LINE_SEP;
  2356.     else if (left != 2 && right != 2 && (left == 1 || right == 1))
  2357.       result += "-" HALF_DOUBLE_LINE_SEP;
  2358.   }
  2359. }
  2360.  
  2361. void table::add_vertical_rule(int start_row, int end_row, int col, int is_double)
  2362. {
  2363.   vrule_list = new vertical_rule(start_row, end_row, col, is_double,
  2364.                  vrule_list);
  2365.   compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust);
  2366.   compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust);
  2367. }
  2368.  
  2369. void table::build_vrule_list()
  2370. {
  2371.   int col;
  2372.   if (flags & ALLBOX) {
  2373.     for (col = 1; col < ncolumns; col++) {
  2374.       int start_row = 0;
  2375.       for (;;) {
  2376.     while (start_row < nrows && vline_spanned(start_row, col))
  2377.       start_row++;
  2378.     if (start_row >= nrows)
  2379.       break;
  2380.     int end_row = start_row;
  2381.     while (end_row < nrows && !vline_spanned(end_row, col))
  2382.       end_row++;
  2383.     end_row--;
  2384.     add_vertical_rule(start_row, end_row, col, 0);
  2385.     start_row = end_row + 1;
  2386.       }
  2387.     }
  2388.   }
  2389.   if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
  2390.     add_vertical_rule(0, nrows - 1, 0, 0);
  2391.     add_vertical_rule(0, nrows - 1, ncolumns, 0);
  2392.   }
  2393.   for (int end_row = 0; end_row < nrows; end_row++)
  2394.     for (col = 0; col < ncolumns+1; col++)
  2395.       if (vline[end_row][col] > 0
  2396.       && !vline_spanned(end_row, col)
  2397.       && (end_row == nrows - 1 
  2398.           || vline[end_row+1][col] != vline[end_row][col]
  2399.           || vline_spanned(end_row+1, col))) {
  2400.     for (int start_row = end_row - 1;
  2401.          start_row >= 0
  2402.          && vline[start_row][col] == vline[end_row][col]
  2403.          && !vline_spanned(start_row, col);
  2404.          start_row--)
  2405.       ;
  2406.     start_row++;
  2407.     add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1);
  2408.       }
  2409.   for (vertical_rule *p = vrule_list; p; p = p->next)
  2410.     if (p->is_double)
  2411.       for (int r = p->start_row; r <= p->end_row; r++) {
  2412.     if (p->col > 0 && entry[r][p->col-1] != 0
  2413.         && entry[r][p->col-1]->end_col == p->col-1) {
  2414.       int is_corner = r == p->start_row || r == p->end_row;
  2415.       entry[r][p->col-1]->note_double_vrule_on_right(is_corner);
  2416.     }
  2417.     if (p->col < ncolumns && entry[r][p->col] != 0
  2418.         && entry[r][p->col]->start_col == p->col) {
  2419.       int is_corner = r == p->start_row || r == p->end_row;
  2420.       entry[r][p->col]->note_double_vrule_on_left(is_corner);
  2421.     }
  2422.       }
  2423. }
  2424.  
  2425. void table::define_bottom_macro()
  2426. {
  2427.   prints(".eo\n"
  2428.      ".de T#\n"
  2429.      ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{"
  2430.      "." REPEATED_VPT_MACRO " 0\n"
  2431.      ".mk " SAVED_VERTICAL_POS_REG "\n");
  2432.   if (flags & (BOX|ALLBOX|DOUBLEBOX)) {
  2433.     prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{");
  2434.     print_single_hline(0);
  2435.     prints(".\\}\n");
  2436.   }
  2437.   prints(".ls 1\n");
  2438.   for (vertical_rule *p = vrule_list; p; p = p->next)
  2439.     p->contribute_to_bottom_macro(this);
  2440.   if (flags & DOUBLEBOX)
  2441.     prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"
  2442.        "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
  2443.        "\\D'l \\n[TW]u 0'\\s0\n"
  2444.        ".vs\n"
  2445.        ".\\}\n"
  2446.        ".if \\n[" LAST_PASSED_ROW_REG "]>=0 "
  2447.        ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n"
  2448.        ".sp -1\n"
  2449.        "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]"
  2450.        "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"
  2451.        ".sp -1\n"
  2452.        "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
  2453.        "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
  2454.   prints(".ls\n");
  2455.   prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
  2456.      ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
  2457.      "." REPEATED_VPT_MACRO " 1\n"
  2458.      ".\\}\n"
  2459.      "..\n"
  2460.      ".ec\n");
  2461. }
  2462.  
  2463.  
  2464. // is the vertical line before column c in row r horizontally spanned?
  2465.  
  2466. int table::vline_spanned(int r, int c)
  2467. {
  2468.   assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1);
  2469.   return (c != 0 && c != ncolumns && entry[r][c] != 0
  2470.       && entry[r][c]->start_col != c
  2471.       // horizontally spanning lines don't count
  2472.       && entry[r][c]->to_double_line_entry() == 0
  2473.       && entry[r][c]->to_single_line_entry() == 0);
  2474. }
  2475.  
  2476. int table::row_begins_section(int r)
  2477. {
  2478.   assert(r >= 0 && r < nrows);
  2479.   for (int i = 0; i < ncolumns; i++)
  2480.     if (entry[r][i] && entry[r][i]->start_row != r)
  2481.       return 0;
  2482.   return 1;
  2483. }
  2484.  
  2485. int table::row_ends_section(int r)
  2486. {
  2487.   assert(r >= 0 && r < nrows);
  2488.   for (int i = 0; i < ncolumns; i++)
  2489.     if (entry[r][i] && entry[r][i]->end_row != r)
  2490.       return 0;
  2491.   return 1;
  2492. }
  2493.  
  2494. void table::do_row(int r)
  2495. {
  2496.   if (!(flags & NOKEEP) && row_begins_section(r))
  2497.     prints("." KEEP_MACRO_NAME "\n");
  2498.   int had_line = 0;
  2499.   for (stuff *p = stuff_list; p && p->row < r; p = p->next)
  2500.     ;
  2501.   for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next)
  2502.     if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) {
  2503.       had_line = 1;
  2504.       break;
  2505.     }
  2506.   if (!had_line && !row_is_all_lines[r])
  2507.     printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
  2508.   had_line = 0;
  2509.   for (; p && p->row == r; p = p->next)
  2510.     if (!p->printed) {
  2511.       p->print(this);
  2512.       if (!had_line && (p->is_single_line() || p->is_double_line())) {
  2513.     printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
  2514.     had_line = 1;
  2515.       }
  2516.     }
  2517.   // Change the row *after* printing the stuff list (which might contain .TH).
  2518.   printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n",
  2519.       as_string(r));
  2520.   if (!had_line && row_is_all_lines[r])
  2521.     printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r));
  2522.   // we might have had a .TH, for example,  since we last tried
  2523.   if (!(flags & NOKEEP) && row_begins_section(r))
  2524.     prints("." KEEP_MACRO_NAME "\n");
  2525.   printfs(".mk %1\n", row_start_reg(r));
  2526.   prints(".mk " BOTTOM_REG "\n"
  2527.      "." REPEATED_VPT_MACRO " 0\n");
  2528.   int c;
  2529.   int row_is_blank = 1;
  2530.   int first_start_row = r;
  2531.   for (c = 0; c < ncolumns; c++) {
  2532.     table_entry *e = entry[r][c];
  2533.     if (e) {
  2534.       if (e->end_row == r) {
  2535.     e->do_depth();
  2536.     if (e->start_row < first_start_row)
  2537.       first_start_row = e->start_row;
  2538.     row_is_blank = 0;
  2539.       }
  2540.       c = e->end_col;
  2541.     }
  2542.   }
  2543.   if (row_is_blank)
  2544.     prints(".nr " BOTTOM_REG " +1v\n");
  2545.   if (row_is_all_lines[r]) {
  2546.     prints(".vs " LINE_SEP);
  2547.     if (row_is_all_lines[r] == 2)
  2548.       prints("+" DOUBLE_LINE_SEP);
  2549.     prints(">?\\n[.V]u\n.ls 1\n");
  2550.     prints("\\&");
  2551.     prints("\\v'" BODY_DEPTH);
  2552.     if (row_is_all_lines[r] == 2)
  2553.       prints("-" HALF_DOUBLE_LINE_SEP);
  2554.     prints("'");
  2555.     for (c = 0; c < ncolumns; c++) {
  2556.       table_entry *e = entry[r][c];
  2557.       if (e) {
  2558.     if (e->end_row == e->start_row)
  2559.       e->to_simple_entry()->simple_print(1);
  2560.     c = e->end_col;
  2561.       }
  2562.     }
  2563.     prints("\n");
  2564.     prints(".ls\n"
  2565.        ".vs\n");
  2566.     prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
  2567.     printfs(".sp |\\n[%1]u\n", row_start_reg(r));
  2568.   }
  2569.   for (int i = row_is_all_lines[r] ? r - 1 : r;
  2570.        i >= first_start_row;
  2571.        i--) {
  2572.     simple_entry *first = 0;
  2573.     for (c = 0; c < ncolumns; c++) {
  2574.       table_entry *e = entry[r][c];
  2575.       if (e) {
  2576.     if (e->end_row == r && e->start_row == i) {
  2577.       simple_entry *simple = e->to_simple_entry();
  2578.       if (simple) {
  2579.         if (!first) {
  2580.           prints(".ta");
  2581.           first = simple;
  2582.         }
  2583.         simple->add_tab();
  2584.       }
  2585.     }
  2586.     c = e->end_col;
  2587.       }
  2588.     }
  2589.     if (first) {
  2590.       prints('\n');
  2591.       first->position_vertically();
  2592.       first->set_location();
  2593.       prints("\\&");
  2594.       first->simple_print(0);
  2595.       for (c = first->end_col + 1; c < ncolumns; c++) {
  2596.     table_entry *e = entry[r][c];
  2597.     if (e) {
  2598.       if (e->end_row == r && e->start_row == i) {
  2599.         simple_entry *simple = e->to_simple_entry();
  2600.         if (simple)
  2601.           simple->simple_print(0);
  2602.       }
  2603.       c = e->end_col;
  2604.     }
  2605.       }
  2606.       prints('\n');
  2607.       prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
  2608.       printfs(".sp |\\n[%1]u\n", row_start_reg(r));
  2609.     }
  2610.   }
  2611.   for (c = 0; c < ncolumns; c++) {
  2612.     table_entry *e = entry[r][c];
  2613.     if (e) {
  2614.       if (e->end_row == r && e->to_simple_entry() == 0) {
  2615.     e->position_vertically();
  2616.     e->print();
  2617.     prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
  2618.     printfs(".sp |\\n[%1]u\n", row_start_reg(r));
  2619.       }
  2620.       c = e->end_col;
  2621.     }
  2622.   }
  2623.   prints("." REPEATED_VPT_MACRO " 1\n"
  2624.      ".sp |\\n[" BOTTOM_REG "]u\n"
  2625.      "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
  2626.   if (r != nrows - 1 && (flags & ALLBOX)) {
  2627.     print_single_hline(r + 1);
  2628.     prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n");
  2629.   }
  2630.   if (r != nrows - 1) {
  2631.     if (p && p->row == r + 1
  2632.     && (p->is_single_line() || p->is_double_line())) {
  2633.       p->print(this);
  2634.       prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG
  2635.          " 0\n");
  2636.     }
  2637.     int printed_one = 0;
  2638.     for (vertical_rule *p = vrule_list; p; p = p->next)
  2639.       if (p->end_row == r) {
  2640.     if (!printed_one) {
  2641.       prints("." REPEATED_VPT_MACRO " 0\n");
  2642.       printed_one = 1;
  2643.     }
  2644.     p->print();
  2645.       }
  2646.     if (printed_one)
  2647.       prints("." REPEATED_VPT_MACRO " 1\n");
  2648.     if (!(flags & NOKEEP) && row_ends_section(r))
  2649.       prints("." RELEASE_MACRO_NAME "\n");
  2650.   }
  2651. }
  2652.  
  2653. void table::do_top()
  2654. {
  2655.   prints(".fc \002\003\n");
  2656.   if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
  2657.     prints("." TABLE_KEEP_MACRO_NAME "\n");
  2658.   if (flags & DOUBLEBOX) {
  2659.     prints(".ls 1\n"
  2660.        ".vs " LINE_SEP ">?\\n[.V]u\n"
  2661.        "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
  2662.        ".vs\n"
  2663.        "." REPEATED_MARK_MACRO " " TOP_REG "\n"
  2664.        ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n");
  2665.     printfs("\\v'" BODY_DEPTH "'"
  2666.         "\\s[\\n[" LINESIZE_REG "]]"
  2667.         "\\h'\\n[%1]u'"
  2668.         "\\D'l |\\n[%2]u 0'"
  2669.         "\\s0"
  2670.         "\n",
  2671.         column_divide_reg(0),
  2672.         column_divide_reg(ncolumns));
  2673.     prints(".ls\n"
  2674.        ".vs\n");
  2675.   }
  2676.   else if (flags & (ALLBOX|BOX)) {
  2677.     print_single_hline(0);
  2678.   }
  2679.   //printfs(".mk %1\n", row_top_reg(0));
  2680. }
  2681.  
  2682. void table::do_bottom()
  2683. {
  2684.   // print stuff after last row
  2685.   for (stuff *p = stuff_list; p; p = p->next)
  2686.     if (p->row > nrows - 1)
  2687.       p->print(this);
  2688.   if (!(flags & NOKEEP))
  2689.     prints("." RELEASE_MACRO_NAME "\n");
  2690.   printfs(".mk %1\n", row_top_reg(nrows));
  2691.   prints(".nr " NEED_BOTTOM_RULE_REG " 1\n"
  2692.      ".nr T. 1\n"
  2693.      ".T#\n");
  2694.   if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX)))
  2695.     prints("." TABLE_RELEASE_MACRO_NAME "\n");
  2696.   if (flags & DOUBLEBOX)
  2697.     prints(".sp " DOUBLE_LINE_SEP "\n");
  2698.   prints("." RESET_MACRO_NAME "\n"
  2699.      ".fc\n"
  2700.      ".cp \\n(" COMPATIBLE_REG "\n");
  2701. }
  2702.  
  2703. int table::get_nrows()
  2704. {
  2705.   return nrows;
  2706. }
  2707.  
  2708. const char *last_filename = 0;
  2709.  
  2710. void set_troff_location(const char *fn, int ln)
  2711. {
  2712.   if (!location_force_filename && last_filename != 0
  2713.       && strcmp(fn, last_filename) == 0)
  2714.     printfs(".lf %1\n", as_string(ln));
  2715.   else {
  2716.     printfs(".lf %1 %2\n", as_string(ln), fn);
  2717.     last_filename = fn;
  2718.     location_force_filename = 0;
  2719.   }
  2720. }
  2721.  
  2722. void printfs(const char *s, const string &arg1, const string &arg2,
  2723.          const string &arg3, const string &arg4, const string &arg5)
  2724. {
  2725.   if (s) {
  2726.     char c;
  2727.     while ((c = *s++) != '\0') {
  2728.       if (c == '%') {
  2729.     switch (*s++) {
  2730.     case '1':
  2731.       prints(arg1);
  2732.       break;
  2733.     case '2':
  2734.       prints(arg2);
  2735.       break;
  2736.     case '3':
  2737.       prints(arg3);
  2738.       break;
  2739.     case '4':
  2740.       prints(arg4);
  2741.       break;
  2742.     case '5':
  2743.       prints(arg5);
  2744.       break;
  2745.     case '6':
  2746.     case '7':
  2747.     case '8':
  2748.     case '9':
  2749.       break;
  2750.     case '%':
  2751.       prints('%');
  2752.       break;
  2753.     default:
  2754.       assert(0);
  2755.     }
  2756.       }
  2757.       else
  2758.     prints(c);
  2759.     }
  2760.   }
  2761. }  
  2762.  
  2763.